"""
一阶导数处理器

计算光谱数据的一阶导数，用于突出光谱特征和消除基线漂移。
一阶导数能够增强光谱的分辨率，突出吸收峰的位置。
"""

import pandas as pd
import numpy as np
from scipy.signal import savgol_filter
from ..base import PreprocessorInterface
from ..validation import validate_input_data, safe_copy_dataframe


class FirstDerivative(PreprocessorInterface):
    """
    一阶导数处理器
    
    计算光谱数据的一阶导数，用于：
    1. 消除基线漂移
    2. 突出光谱特征
    3. 增强光谱分辨率
    """
    
    def __init__(self, method='savgol', window_length=11, polyorder=2):
        """
        初始化一阶导数参数
        
        Args:
            method (str): 计算方法，'savgol'或'diff'
            window_length (int): SG滤波窗口长度（仅用于savgol方法）
            polyorder (int): 多项式阶数（仅用于savgol方法）
        """
        self.method = method
        
        # 确保窗口长度为奇数
        if window_length % 2 == 0:
            window_length += 1
        
        self.window_length = window_length
        self.polyorder = polyorder
    
    def process(self, data: pd.DataFrame) -> pd.DataFrame:
        """
        计算光谱数据的一阶导数
        
        Args:
            data (pd.DataFrame): 输入光谱数据
            
        Returns:
            pd.DataFrame: 一阶导数后的数据
        """
        # 验证输入数据
        validate_input_data(data)
        
        # 创建数据副本
        result = safe_copy_dataframe(data)
        
        # 获取光谱数据列（跳过第一列ID列）
        spectral_columns = result.columns[1:]
        spectral_data = result[spectral_columns].values
        
        # 计算一阶导数
        if self.method == 'savgol':
            derivative_spectra = self._savgol_derivative(spectral_data)
        else:  # 使用简单差分方法
            derivative_spectra = self._diff_derivative(spectral_data)
        
        # 将导数数据放回DataFrame
        result[spectral_columns] = derivative_spectra
        
        return result
    
    def _savgol_derivative(self, spectral_data):
        """使用Savitzky-Golay方法计算一阶导数"""
        n_samples, n_points = spectral_data.shape
        
        # 检查窗口长度
        window_length = min(self.window_length, n_points)
        if window_length % 2 == 0:
            window_length -= 1
        window_length = max(window_length, self.polyorder + 1)
        
        derivative_spectra = np.zeros_like(spectral_data)
        
        for i in range(n_samples):
            spectrum = spectral_data[i, :]
            
            try:
                # 使用SG滤波器计算一阶导数
                derivative = savgol_filter(
                    spectrum,
                    window_length=window_length,
                    polyorder=self.polyorder,
                    deriv=1,  # 一阶导数
                    mode='nearest'
                )
                derivative_spectra[i, :] = derivative
            except Exception as e:
                # 如果SG方法失败，使用差分方法
                print(f"SG一阶导数失败，样本 {i}，使用差分方法: {e}")
                derivative_spectra[i, :] = self._diff_single_spectrum(spectrum)
        
        return derivative_spectra
    
    def _diff_derivative(self, spectral_data):
        """使用差分方法计算一阶导数"""
        n_samples, n_points = spectral_data.shape
        derivative_spectra = np.zeros_like(spectral_data)
        
        for i in range(n_samples):
            spectrum = spectral_data[i, :]
            derivative_spectra[i, :] = self._diff_single_spectrum(spectrum)
        
        return derivative_spectra
    
    def _diff_single_spectrum(self, spectrum):
        """对单个光谱计算差分导数"""
        derivative = np.zeros_like(spectrum)
        
        # 前向差分：d[i] = x[i+1] - x[i]
        derivative[:-1] = np.diff(spectrum)
        
        # 最后一个点使用后向差分
        derivative[-1] = spectrum[-1] - spectrum[-2]
        
        return derivative
    
    def get_name(self) -> str:
        """返回处理器名称"""
        if self.method == 'savgol':
            return f"一阶导数(SG窗口{self.window_length})"
        else:
            return "一阶导数(差分)"
    
    def get_id(self) -> int:
        """返回唯一ID"""
        return 7